import { isObj } from "./util";

interface Page {
  data: object;
  setData: Function;
}

type CallBack = (oV: any, nV: any) => undefined | any;

interface CallBackObj {
  [p: string]: CallBack;
}

export function observer(
  data: any,
  page: Page,
  cb?: CallBack | CallBackObj,
  isShallow = true
) {
  const proxy = new Proxy(data, {
    get(target, key) {
      if (key == "__ob__") return true;
      const value = Reflect.get(target, key);
      return value;
    },
    set(target: any, key, value, _) {
      let oldValue = target[key];
      let result = watch(oldValue, value, key, cb, page);
      if (typeof result !== "undefined") {
        value = result;
      }
      if (result === value) return true;
      if (value && typeof value === "object" && !value.__ob__) {
        if (isObj(value)) {
          result = observer(value, page, cb, false);
        } else if (Array.isArray(value)) {
          value.forEach((item, index) => {
            if (isObj(item) && !item.__ob__) {
              value[index] = observer(item, page, cb, false);
            }
          });
        }
      } else {
        target[key] = value;
      }
      if (isShallow) {
        page.setData({ [key]: value });
      } else {
        watch(oldValue, value, key, cb, page);
        page.setData({ ...page.data });
      }

      return true;
    },
  });
  return proxy;
}

export const watch = (
  oV: any,
  nV: any,
  key: symbol | string,
  cb?: any | { [pro: string | symbol]: Function },
  page?: any
) => {
  if (!cb || !page) return;
  if (isObj(cb)) {
    if (typeof cb[key] === "function") {
      return cb[key].call(page, oV, nV);
    }
  } else {
    return cb.call(page, oV, nV);
  }
};

//废弃
export function defineReactive(data: any, page: Page, isShallow = true) {
  if (isObj(data)) {
    Object.keys(data).forEach((key) => {
      Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get() {
          return data[key];
        },
        set(value) {
          if (isShallow) {
            page.setData({ [key]: value });
          } else {
            page.setData({ ...page.data });
          }
        },
      });
    });
  }
}
